package calculation;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import my_interface.SelectType;
import my_interface.WindowType;
import org.apache.mahout.math.jet.math.Bessel;

/**
 *
 * @author KieuOanh
 */
public class FIRWindow implements SelectType, WindowType {

    public int typeWindow;
    public int typeSelect;
    //
    public double wp;
    public double ws;
    public double As;
    public double Rp;
    //
    public double w2p;
    public double w2s;
    //
    public double wc;
    public double w2c;
    public final double pi = Math.PI;
    public final double pi2 = 2 * Math.PI;
    public double tr_width;
    //
    //khai bao bien cho cua so 
    public int M;
    public double alpha;
    public double[] hd;
    public double[] hn;
    public double[] w;
    public double[] dB;
    public double[] Hw;
    public double beta;
    public double hdMax = 0;
    public double hnMax = 0;
    public double hdMin = 0;
    public double hnMin = 0;
    public double wnMax = 0;
    public double dBMax = 0;
    public double dBMin = 0;
    public double HwMax = 0;
    public final double eps = 1.0 / 5.0 + 1.0 / 5.0 - 1.0 / 10.0 - 1.0 / 10.0 - 1.0 / 10.0 - 1.0 / 10.0;

    public FIRWindow(int typeSelect, int typeWindow, double wp, double ws, double As, double Rp, double w2p, double w2s) {
        this.typeWindow = typeWindow;
        this.typeSelect = typeSelect;
        this.wp = wp;
        this.ws = ws;
        this.As = As;
        this.Rp = Rp;
        this.w2p = w2p;
        this.w2s = w2s;

        wc = pi * (wp + ws) / 2;
        w2c = pi * (w2p + w2s) / 2;

        ////////////////////////
        setValueM();
        contructArrayH(M);
        setValueWnHdhn();
        findMaxMin();
        setValueDB(M);
        //////////
        System.out.println("M: " + M);
    }

    public FIRWindow(int typeSelect, int typeWindow, double wp, double ws, double As, double Rp) {
        this.typeWindow = typeWindow;
        this.typeSelect = typeSelect;
        this.wp = wp;
        this.ws = ws;
        this.As = As;
        this.Rp = Rp;
        wc = pi * (wp + ws) / 2;

        dB = new double[300];
        Hw = new double[300];
        //////////////////////////////////
        setValueM();
        contructArrayH(M);
        setValueWnHdhn();
        findMaxMin();
        setValueDB(M);
        //
        System.out.println("M: " + M);
    }

    public final int setValueM() {
        switch (typeSelect) {
            case LOW_PASS:
                tr_width = (ws - wp) * pi;
                break;
            case HIGH_PASS:
                tr_width = (wp - ws) * pi;
                break;
            case BAND_PASS:
                tr_width = min((wp - ws) * pi, (w2s - w2p) * pi);
                break;
            case BAND_STOP:
                tr_width = min((ws - wp) * pi, (w2p - w2s) * pi);
                break;
        }
        switch (typeWindow) {
            case RECTANGLE:
                M = round((1.8 * pi / tr_width)) + 1;
                break;
            case BARTLETT:
                M = round((6.1 * pi / tr_width)) + 1;
                break;
            case HANNING:
                M = round((6.2 * pi / tr_width)) + 1;
                break;
            case HAMMING:
                M = round((6.6 * pi / tr_width)) + 1;
                break;
            case BLACKMAN:
                M = round((11 * pi / tr_width)) + 1;
                break;
            case KAISER:
                M = round(((As - 7.95) * pi2 / (14.36 * tr_width) + 1)) + 1;
                break;
        }
        return M;
    }

    public void setValueWnHdhn() {
         FileWriter fstream = null;
        try {
            fstream = new FileWriter("D://FIR Window.txt");
        } catch (IOException ex) {
            Logger.getLogger(FIRFrequenceSample.class.getName()).log(Level.SEVERE, null, ex);
        }
        BufferedWriter out = new BufferedWriter(fstream);
        for (int i = 0; i < M; i++) {
            switch (typeWindow) {
                case RECTANGLE:
                    w[i] = 1;
                    break;
                case BARTLETT:
                    if (i <= (M - 1) / 2) {
                        w[i] = 2 * i / (double) (M - 1);
                    } else {
                        w[i] = 2 - 2 * i / (double) (M - 1);
                    }
                    break;
                case HANNING:
                    w[i] = 0.5 - 0.5 * cos(pi2 / (M - 1) * i);
                    break;
                case HAMMING:
                    w[i] = 0.54 - 0.46 * cos(pi2 / (M - 1) * i);
                    break;
                case BLACKMAN:
                    w[i] = 0.42 - 0.5 * cos(pi2 / (M - 1) * i) + 0.08 * cos(4 * pi / (M - 1) * i);
                    break;
                case KAISER:
                    if (As >= 50) {
                        beta = 0.1102 * (As - 8.7);
                    } else if (21 < As && As < 50) {
                        beta = 0.5842 * Math.pow(As - 21, 0.4) + 0.07886 * (As - 21);
                    }
                    w[i] = Bessel.i0(beta * sqrt(1 - Math.pow(1 - (double) i * 2 / (M - 1), 2))) / Bessel.i0(beta);
                    break;
            }
            switch (typeSelect) {
                case LOW_PASS:
                    hd[i] = sin(wc * (i - alpha + eps)) / (pi * (i - alpha + eps));
                    break;
                case HIGH_PASS:
                    hd[i] = (sin(pi * (i - alpha + eps)) - sin(wc * (i - alpha + eps))) / (pi * (i - alpha + eps));
                    break;
                case BAND_PASS:
                    hd[i] = (sin(w2c * (i - alpha + eps)) - sin(wc * (i - alpha + eps))) / (pi * (i - alpha + eps));
                    break;
                case BAND_STOP:
                    hd[i] = (sin(pi * (i - alpha + eps)) - sin(w2c * (i - alpha + eps)) + sin(wc * (i - alpha + eps))) / (pi * (i - alpha + eps));
                    break;
            }
            hn[i] = hd[i] * w[i];
             try {
                out.write("\nhn[" + i + "]= " + hn[i]);
            } catch (IOException ex) {
                Logger.getLogger(FIRFrequenceSample.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
 try {
            out.close();
        } catch (IOException ex) {
            Logger.getLogger(FIRFrequenceSample.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void findMaxMin() {
        for (int i = 0; i < M; i++) {
            hdMax = max(hdMax, hd[i]);
            hnMax = max(hnMax, hn[i]);
            hdMin = min(hdMin, hd[i]);
            hnMin = min(hnMin, hn[i]);
            wnMax = max(wnMax, w[i]);
        }

    }

    public void contructArrayH(int M) {
        alpha = (M - 1) / 2;
        hd = new double[M];
        w = new double[M];
        hn = new double[M];
        Hw = new double[300];
        dB = new double[300];
    }

    public void setValueDB(int M) {
        double real = 0;
        double imag = 0;
        for (int w = 0; w < 300; w++) {
            real = 0;
            imag = 0;
            for (int n = 0; n < M; n++) {
                real += hn[n] * cos(pi / 300 * w * n);
                imag += hn[n] * sin(pi / 300 * w * n);
            }

            Hw[w] = module(real, imag);
            HwMax = max(HwMax, Hw[w]);            
        }
        for (int w = 0; w < 300; w++) {
            dB[w] = -20 * log10(Hw[w] / HwMax);
            dBMax = max(dBMax, dB[w]);
        }
        dBMin = dBMax;
        double rp = 0.0;
        int tempWs = 0;
        int tempW2s = 0;
        int tempWp = 0;
        int tempW2p = 0;
        tempWs = (int) (ws * 300);
        tempWp = (int) (wp * 300) + 1;
        tempW2s = (int) (w2s * 300);
        tempW2p = (int) (w2p * 300);
        switch (typeSelect) {
            case LOW_PASS:
                for (int i = tempWs; i < 300; i++) {
                    dBMin = min(dBMin, dB[i]);
                }
                for (int i = 0; i < tempWp; i++) {
                    rp = min(dBMax, dB[i]);
                }
                break;
            case HIGH_PASS:
                for (int i = 0; i < tempWs; i++) {
                    dBMin = min(dBMin, dB[i]);
                }
                for (int i = tempWp; i < 300; i++) {
                    rp = min(dBMax, dB[i]);
                }
                break;
            case BAND_PASS:
                for (int i = 0; i < tempWs; i++) {
                    dBMin = min(dBMin, dB[i]);
                }
                for (int i = tempWp; i < tempW2p; i++) {
                    rp = min(dBMax, dB[i]);
                }
                break;
            case BAND_STOP:
                for (int i = 0; i < tempWp; i++) {
                    rp = min(dBMax, dB[i]);
                }
                for (int i = tempWs; i < tempW2s; i++) {
                    dBMin = min(dBMin, dB[i]);
                }
                break;
        }
        System.out.println("dBMax= " + dBMax);
        System.out.println("As= " + dBMin);
        System.out.println("Rp= " + rp);
    }

    double max(double x, double y) {
        if (x > y) {
            return x;
        } else {
            return y;
        }
    }

    double min(double x, double y) {
        if (x < y) {
            return x;
        } else {
            return y;
        }
    }

    double sin(double x) {
        return Math.sin(x);
    }

    double cos(double x) {
        return Math.cos(x);
    }

    int round(double x) {
        return (int) Math.round(x);
    }

    double log10(double x) {
        return Math.log10(x);
    }

    double sqrt(double x) {
        return Math.sqrt(x);
    }

    double module(double real, double imag) {
        return sqrt(real * real + imag * imag);
    }
}
